#!/usr/bin/perl -w
# Bug reports and comments to Igor.Ermolaev@intel.com
use strict;
no warnings "portable";

use constant { FALSE=>0, TRUE=>-1 };

my @blocks      = ();
my $min_addr    = undef;
my $max_addr    = undef;
my $sep         = FALSE;
my $src_line    = undef;
my $all_loops   = FALSE;
my $instrs_bfr  = undef;
my $instrs_aft  = undef;
my $need_lines  = FALSE;
my $need_sep    = FALSE;
my $need_filt   = FALSE;
my $need_perf   = FALSE;
my $nesting     = 0;
my $vec_pos     = undef;
my $vec_idx     = undef;
my $loop_pos    = undef;
my $loop_idx    = undef;
my $focus_scope = undef;

my $i;
for( $i = 0; $i <= $#ARGV; $i++ )
{
   $_ = $ARGV[$i];
   if( /^(\d+)(\:(\d+))?$/o && $sep == FALSE )
   {
      push @blocks, { addr=>$1, size=>$3 };
      $min_addr = $1 if !defined $min_addr || $1 < $min_addr;
      my $addr  = defined $3 ? $1 + $3 - 1 : $1;
      $max_addr = $addr if !defined $max_addr || $addr > $max_addr;
   }
   elsif( $_ eq "--" )
   {
      $sep = TRUE;
   }
   elsif( $_ eq "-a" )
   {
      $all_loops = TRUE;
      $sep       = TRUE;
   }
   elsif( /^[^:]+(\:\d+)?/o && $sep == FALSE )
   {
      $src_line = $_;
      $sep      = TRUE;
   }
   elsif( /^\d+$/o )
   {
      if( defined $instrs_bfr )
      {
         $instrs_aft = $_;
      }
      else
      {
         $instrs_bfr = $_;
      }
   }
   elsif( $_ eq "-l" )
   {
      $need_lines = TRUE;
   }
   elsif( $_ eq "-s" )
   {
      $need_sep = TRUE;
   }
   elsif( $_ eq "-f" )
   {
      $need_filt = TRUE;
   }
   elsif( $_ eq "-n" )
   {
      $nesting = $ARGV[$i+1];
      $i++;
   }
   elsif( $_ eq "-v" )
   {
      $vec_pos = $ARGV[$i+1];
      $vec_idx = -1;
      $i++;
   }
   elsif( $_ eq "-i" )
   {
      $loop_pos = $ARGV[$i+1];
      $loop_idx = -1;
      $i++;
   }
   elsif( $_ eq "-p" )
   {
      $need_perf = TRUE;
   }
   elsif( $_ eq "-r" )
   {
      $focus_scope = $ARGV[$i+1];
      $i++;
   }
   else
   {
      last;
   }
}
if( $i <= $#ARGV || !@ARGV || ( !defined $src_line && $all_loops == FALSE && !@blocks ) )
{
   print "Usage: get_loop.pl <file>:<line> | -a | <addr>:<size>... [ <instructions after loop> [ <instructions before loop> ] ] [ -l ] [ -s ] [ -f ] [ -p ] [ -n <# of nested loops> ] [ -v <index of vectorized loop> ]\n";
   exit( -1 );
}
$instrs_bfr = 0 if !defined $instrs_bfr;
$instrs_aft = 0 if !defined $instrs_aft;

print "; ", join( " ", @ARGV ), "\n";

my @cache      = ();
my $cache_on   = FALSE;
my %addrs      = ();
my @loop       = ();
my $loop_0     = undef;
my $loop_N     = undef;
my $last_seen  = -1;
my $rest       = -1;
my $file       = defined $src_line ? ( $_ = $src_line, s/\:\d+$//, $_ ) : undef;
my @hierarchy  = ( [] );
my $function   = undef;
my %aliases    = ();
my $alias_ask  = FALSE;
my $inlined    = undef;
my $openmped   = undef;
my $scope      = undef;
my $lline      = undef;
my $need_flow  = FALSE;
my $jmp_tgt    = undef;

use constant {
   ASM_LINE=>qr/^\s*([[:xdigit:]]+)\:\s*([[:xdigit:]][[:xdigit:]]\s+)*\s*(.+)$/o,
   FLW_INST=>qr/^([\w,]+)\s+\$?(0x)?([[:xdigit:]]+)\s*([<#]|$)/o
};

while( <STDIN> )
{
   if( $last_seen != -1 && $#cache - $last_seen + 1 > 10000 )
   {
      @cache     = ();
      $cache_on  = FALSE;
      $last_seen = -1;
      %addrs     = ();
      @hierarchy = ( [] );
      $need_flow = FALSE;
   }
   if( /^(0x)?[[:xdigit:]]+\s*\<([^\>\+]+)(\+(0x)?[[:xdigit:]]+)?\>\:/o )
   {
      my $sym = $2;
      if( defined $function && ( $sym =~ /^\../o || $sym =~ /^L\d+$/ ) )
      {
         $inlined = $sym;
         $scope   = $function.':'.$sym;
         $scope  .= ':'.$openmped if defined $openmped;
      }
      else
      {
         $function  = $sym;
         $scope     = $sym; # should I reset inl and omp as well?
         %aliases   = ();
         $alias_ask = TRUE;
      }
      $cache_on = TRUE if defined $src_line;
   }
   elsif( /^([^\(]+)\([^\)]*\)\:$/o )
   {
      if( $1 eq $function )
      {
         $inlined = undef;
      }
      else
      {
         my $inl = $1;
         my $omp = $inl;$omp =~ s/^(L_.+__par_[^\d]+)\d.*$/$1/io;
         if( $inl eq $omp )
         {
            if( exists $aliases{$inl} )
            {
               $inlined = undef;
            }
            else
            {
               if( $alias_ask == TRUE )
               {
                  $aliases{$inl} = TRUE;
               }
               else
               {
                  $inlined = $inl;
               }
            }
         }
         else
         {
            $openmped = $omp;
         }
      }
      $scope  = $function;
      $scope .= ':'.$inlined  if defined $inlined;
      $scope .= ':'.$openmped if defined $openmped;
      $cache_on = TRUE if defined $src_line;
   }
   elsif( /\:(\d+)$/o )
   {
      $lline = int $1;
   }
   elsif( /\<__kmpc_for_static_init/io )
   {
      $openmped = 'L_'.$function.'_'.$lline.'__par_loop' if !( $scope =~ /\:L_.+__par_[^\d]$/o );

      $scope  = $function;
      $scope .= ':'.$inlined  if defined $inlined;
      $scope .= ':'.$openmped if defined $openmped;

      $need_flow = TRUE;
      $jmp_tgt   = undef;
   }
   elsif( /^\s*(0x)?([[:xdigit:]]+)/o )
   {
      $alias_ask = FALSE;
   }
   next if $need_filt && ( /^\s*$/o || /.*\:$/o || /^;---/o );
   if( defined $src_line )
   {
      $last_seen = $#cache + 1 if /(^|\/)\Q$src_line\E$/o;
      $cache_on  = TRUE if /(^|\/)\Q$file\E\:/o; 
   }
   elsif( $all_loops == TRUE )
   {
      $last_seen = $#cache + 1;
      $cache_on  = TRUE;
   }
   elsif( /^\s*(0x)?([[:xdigit:]]+)/o )
   {
      my $cur = hex $2;
      if( $cur >= $min_addr - 16384 && $cur <= $max_addr )
      {
         for my $b (@blocks)
         {
            if( defined $b->{size} )
            {
               $last_seen = $#cache + 1 if $cur >= $b->{addr} && $cur < $b->{addr} + $b->{size};
               $cache_on  = TRUE if $cur >= $b->{addr} - 16384 && $cur < $b->{addr} + $b->{size};
            }
            else
            {
               $last_seen = $#cache + 1 if $cur == $b->{addr};
               $cache_on  = TRUE if $cur >= $b->{addr} - 16384 && $cur <= $b->{addr};
            }
            last if $last_seen == $#cache + 1;
         }
      }
   }
   #print "$cache_on $last_seen: $_";
   if( $cache_on || $rest >= 0 || $need_flow )
   {
      next unless $need_lines || !/\:\d+$/o;
      push @cache, $_ if $cache_on;
      push @loop , $_ if $rest >= 0;
      if( $_ =~ ASM_LINE )
      {
         if( $cache_on || $need_flow )
         {
            my $addr = $1; # todo: avoid potential problem here
            my $line = $3;
            $addrs{$addr} = $#cache if $cache_on;
            if( $need_flow && $line =~ /^ret/io && ( !defined $jmp_tgt || $jmp_tgt <= hex $addr ) )
            {
               #print "<eof> $addr: $line ($scope)\n";
               $scope     = $function;
               $inlined   = undef;
               $openmped  = undef;
               $need_flow = FALSE;
            }
            if( $last_seen >= 0 || $need_flow )
            {
               if( $line =~ FLW_INST )
               {
                  my $instr = $1;
                  my $tgt   = $3;
                  if( $instr =~ /^j[\w,]+/io )
                  {
                     if( $last_seen >= 0 && hex $tgt < hex $addr )
                     {
                        #print "loop detected: $tgt <- $addr\n";
                        my $false_alarm = FALSE;
                        if( $instr =~ /^jmp[\w,]*/io )
                        {
                           $false_alarm = TRUE;
                           for( my $i = $#cache-1; $i >= 0; $i-- )
                           {
                              if( $cache[$i] =~ ASM_LINE )
                              {
                                 my $iaddr = $1;
                                 my $iline = $3;
                                 if( $iline =~ FLW_INST )
                                 {
                                    #my $iinstr = $1;
                                    my $itgt   = $3;
                                    $false_alarm = FALSE if hex $itgt > hex $addr;
                                    #$false_alarm = !( $iinstr =~ /^jmp[\w,]*/io ) && hex $itgt > hex $addr ? FALSE : TRUE;
                                    last;
                                 }
                                 elsif( !( $iline =~ /^(\w?mov|nop|pause)\w*/io ) )
                                 {
                                    if( $iline =~ /^\w*xor\w*\s+(.+)/io )
                                    {
                                       my @ops_arr  = split /\s*,\s*/, $1;
                                       my $the_same = TRUE;for my $op ( @ops_arr ) { if( $op ne $ops_arr[0] ) { $the_same = FALSE; last; } }
                                       next if $the_same == TRUE;
                                    }
                                    last;
                                 }
                              }
                           }
                        }
                        if( $false_alarm == TRUE )
                        {
                           #$last_seen = -1;
                        }
                        elsif( $instr =~ /^j[\w,]*/io && $#cache > 0 && $cache[$#cache-1] =~ /\(bad\)/io ) # remove when bintutils ready
                        {
                           #$last_seen = -1;
                        }
                        elsif( $instr =~ /^jk[\w,]*/io && exists $addrs{$tgt} && $cache[$addrs{$tgt}] =~ /vgather|vscatter/io )
                        {
                           #$last_seen = -1;
                        }
                        elsif( $instr =~ /^j[\w,]*/io && $#cache > 0 && $cache[$#cache-1] =~ /kortest/io && exists $addrs{$tgt} && $cache[$addrs{$tgt}] =~ /vgather|vscatter/io )
                        {
                           #$last_seen = -1;
                        }
                        elsif( exists $addrs{$tgt} )
                        {
                           #print "loop in cache\n";
                           #print @cache;
                           $jmp_tgt = undef;
                           my $from = $addrs{ $tgt };
                           my $idx  = $from;
                           if( defined $src_line )
                           {
                              for( ; $idx > $last_seen; $idx-- )
                              {
                                 last unless $cache[$idx] =~ ASM_LINE; # or file:line pattern mismatch
                              }
                           }
                           elsif( !$all_loops )
                           {
                              my $loop_beg = hex $tgt;
                              my $loop_end = hex $addr;
                              for my $b (@blocks)
                              {
                                 if( $b->{addr} < $loop_beg || $b->{addr} > $loop_end )
                                 {
                                    $idx = $last_seen + 1; # flag to not process this loop
                                    last;
                                 }
                              }
                           }
                           if( $idx <= $last_seen )
                           {
                              my $focus_addr = undef;
                              for( my $i = $last_seen; $i <= $#cache; $i++ )
                              {
                                 if( $cache[$i] =~ ASM_LINE )
                                 {
                                    $focus_addr = hex $1;
                                    last;
                                 }
                              }
                              my $ljmp_tgt = undef;
                              # check for jump to inside of loops body
                              for( my $i = $from-1; $i >= 0; $i-- )
                              {
                                 if( $cache[$i] =~ ASM_LINE )
                                 {
                                    my $cur_addr  = hex $1;
                                    my $cur_instr = $3;
                                    if( $cur_instr =~ FLW_INST )
                                    {
                                       $ljmp_tgt = hex $3 if hex $3 >= hex $tgt && hex $3 <= $focus_addr;
                                       last;
                                    }
                                    elsif( $cur_instr =~ /^ret/io )
                                    {
                                       last;
                                    }
                                 }
                              }
                              for( my $i = $from; $i < $last_seen; $i++ )
                              {
                                 if( $cache[$i] =~ ASM_LINE )
                                 {
                                    my $cur_addr  = hex $1;
                                    my $cur_instr = $3;
                                    if( $cur_instr =~ FLW_INST )
                                    {
                                       my $tgt_addr = hex $3;
                                       if( $tgt_addr > $cur_addr )
                                       {
                                          #print "FI: $cache[$i]";
                                          if( $tgt_addr > $focus_addr )
                                          {
                                             if( $1 =~ /^jmp[\w,]*/io && ( !defined $ljmp_tgt || $ljmp_tgt <= $cur_addr ) )
                                             {
                                                #print "SD: $cache[$i]";
                                                $idx = $last_seen + 1; # flag to not process this loop
                                                last;
                                             }
                                          }
                                          if( defined $ljmp_tgt && $ljmp_tgt > $cur_addr )
                                          {
                                             $ljmp_tgt = $tgt_addr if $tgt_addr > $ljmp_tgt;
                                          }
                                          else
                                          {
                                             $ljmp_tgt = $tgt_addr;
                                             #print "LJ: $cache[$i]";
                                          }
                                       }
                                       else
                                       {
                                          if( $1 =~ /^jmp[\w,]*/io && ( !defined $ljmp_tgt || $ljmp_tgt <= $cur_addr ) )
                                          {
                                             #print "SU: $cache[$i]";
                                             $idx = $last_seen + 1; # flag to not process this loop
                                             last;
                                          }
                                       }
                                    }
                                    elsif( $cur_instr =~ /^ret/io )
                                    {
                                       if( !defined $ljmp_tgt || $ljmp_tgt <= $cur_addr )
                                       {
                                           #print "SR: $cache[$i]";
                                           $idx = $last_seen + 1; # flag to not process this loop
                                           last;
                                       }
                                    }
                                 }
                              }
                           }
                           if( $idx <= $last_seen )
                           {
                              #print $cache[$last_seen-1],$cache[$last_seen],$cache[$last_seen+1];
                              #print "line in loop\n";
                              my $cnt        = 0;
                              my $level      = $#hierarchy+1;
                              my $in_scope   = !defined $focus_scope || $scope eq $focus_scope ? TRUE : FALSE;
                              my $scope_sptd = FALSE;
                              for( ; $level > 0; $level-- )
                              {
                                 for my $l ( @{$hierarchy[$level-1]} )
                                 {
                                    #printf "L%d: %x <- %x\n", $level, $l->{first}, $l->{last};
                                    if( $l->{first} >= hex $tgt && $l->{last} <= hex $addr && ( $in_scope == TRUE || $level != $nesting || $l->{in_scope} == TRUE ) )
                                    {
                                       $cnt++;
                                       $scope_sptd = TRUE if $l->{in_scope} == TRUE;
                                    }
                                 }
                                 last if $cnt != 0;
                              }
                              $in_scope = TRUE if $scope_sptd == TRUE;
                              #print "loop in scope at level $level\n" if $in_scope == TRUE;
                              push @hierarchy, [] if $level > $#hierarchy;
                              push @{$hierarchy[$level]}, { first=>hex $tgt, last=>hex $addr, in_scope=>$in_scope };
                              if( $level == $nesting && ( $cnt == 0 && $level == 0 || $cnt != 0 && $level != 0 ) && $in_scope == TRUE )
                              {
                                 #print "nesting level: $level\n";
                                 if( $need_perf )
                                 {
                                    for( my $i = $from; $i < $#cache; $i++ )
                                    {
                                       if( $cache[$i] =~ ASM_LINE )
                                       {
                                          if( $3 =~ FLW_INST )
                                          {
                                             my $cur_instr = $1;
                                             my $cur_tgt   = $3;
                                             if( $cur_instr =~ /^j[\w,]+/io )
                                             {
                                                next if $i > 0 && $cache[$i-1] =~ /\(bad\)/io; # remove when bintutils ready
                                                if( !exists $addrs{ $cur_tgt } || $addrs{ $cur_tgt } < $from )
                                                {
                                                   $idx = $last_seen+1; # flag to not process this loop
                                                   last;
                                                }
                                             }
                                          }
                                       }
                                    }
                                 }
                                 if( $idx <= $last_seen )
                                 {
                                    @loop = @cache[ $from .. $#cache ];
                                    if( defined $vec_pos )
                                    {
                                       my $veci_seen = FALSE;
                                       my $mmov_seen = FALSE;
                                       my $padd_seen = FALSE;
                                       my $padd_dst  = undef;
                                       my $pcmp_seen = FALSE;
                                       my $pcmp_src  = undef;
                                       my $pcnf_seen = FALSE;
                                       for (@loop)
                                       {
                                          if( $_ =~ ASM_LINE )
                                          {
                                             my $iline = $3;
                                             if( $iline =~ /^\w+((gather|scatter)[d]p[sd]|mov(([ua]p[sd])|(dq[ua]\d+)))\s+(.+)$/io )
                                             {
                                                my $ops = $6;
                                                if( $nesting <= 1 &&
                                                    ( $ops =~ /^[^%].+,\s*\%zmm\d+\s*\{\s*\%k\d+\s*\}(\s*\{\s*z\s*\})?$/io ||
                                                      $ops =~ /^\s*\%zmm\d+[^{]*\{\s*\%k\d+\s*\}\s*$/io ) )
                                                {
                                                   $mmov_seen = TRUE;
                                                   #print "MMOV: $iline\n";
                                                   last if $padd_seen == TRUE && $pcmp_seen == TRUE && ( $nesting == 0 || $pcnf_seen == TRUE );
                                                }
                                             }
                                             elsif( $iline =~ /^((\w+p[sd])|(v?p\w+))\s+(.+)$/io && $iline !~ /^pause/io )
                                             {
                                                my $instr = $1;
                                                my $ops   = $4;
                                                   $ops   =~ s/\s*(#.*)?$//o;
                                                if( $instr =~ /^\w?xorp[sd]/io || $instr =~ /^\w?pxor/io )
                                                {
                                                   my @ops_arr  = split /\s*,\s*/, $ops;
                                                   my $the_same = TRUE;for my $op ( @ops_arr ) { if( $op ne $ops_arr[0] ) { $the_same = FALSE; last; } }
                                                   next if $the_same == TRUE;
                                                }
                                                if( $nesting <= 1 && $instr =~ /^\w?padd/io )
                                                {
                                                   my @ops_arr = split /\s*,\s*/, $ops;splice @ops_arr,0,1;
                                                   my $the_same = TRUE;for my $op ( @ops_arr ) { if( $op ne $ops_arr[0] ) { $the_same = FALSE; last; } }
                                                   if( $the_same == TRUE && $ops_arr[0] =~ /^%[yz]mm\d+$/io )
                                                   {
                                                      $padd_seen = TRUE;
                                                      #print "PADD: $iline\n";
                                                      $pcmp_seen = TRUE if defined $pcmp_src && $pcmp_src eq $ops_arr[0];
                                                      $padd_dst  = $ops_arr[0] if $pcmp_seen == FALSE;
                                                      last if $mmov_seen == TRUE && $pcmp_seen == TRUE && ( $nesting == 0 || $pcnf_seen == TRUE );
                                                   }
                                                }
                                                if( $nesting <= 1 && ( $instr =~ /^\w?pcmp(gt|l[et])/io ) )
                                                {
                                                   my $ct = lc $1;
                                                   my @ops_arr = split /\s*,\s*/, $ops;
                                                   if( $ct eq 'gt' )
                                                   {
                                                      $pcmp_seen = TRUE;
                                                      #print "PCMP: $iline\n";
                                                   }
                                                   elsif( $ct eq 'le' || $ct eq 'lt' )
                                                   {
                                                      if( $ops_arr[0] =~ /^[^%]/io || defined $padd_dst && $padd_dst eq $ops_arr[1] )
                                                      {
                                                         $pcmp_seen = TRUE;
                                                         #print "PCMP: $iline\n";
                                                      }
                                                      else
                                                      {
                                                         $pcmp_src = $ops_arr[1];
                                                         next;
                                                      }
                                                   }
                                                   last if $mmov_seen == TRUE && $padd_seen == TRUE && ( $nesting == 0 || $pcnf_seen == TRUE );
                                                }
                                                if( $nesting == 1 && $instr =~ /^\w?pconflict/io )
                                                {
                                                   $pcnf_seen = TRUE;
                                                   #print "PCNF: $iline\n";
                                                   last if $mmov_seen == TRUE && $padd_seen == TRUE && $pcmp_seen == TRUE;
                                                }
                                                next if $instr =~ /^p(ush|op)/io;
                                                $veci_seen = TRUE;
                                                last if $nesting > 1;
                                             }
                                          }
                                       }
                                       $veci_seen = FALSE if $mmov_seen == TRUE && $padd_seen == TRUE && $pcmp_seen == TRUE && ( $nesting == 0 || $pcnf_seen == TRUE );
                                       $vec_idx++ if $veci_seen == TRUE;
                                    }
                                    $loop_idx++ if defined $loop_pos;
                                    if( ( defined $vec_pos && $vec_idx != $vec_pos ) || ( defined $loop_pos && $loop_idx != $loop_pos ) )
                                    {
                                       @hierarchy = ( [] );
                                    }
                                    else
                                    {
                                       $vec_pos  = -1 if defined $vec_pos;
                                       $loop_pos = -1 if defined $loop_pos;
                                       my $prev  = $instrs_bfr;
                                       my $name  = defined $scope ? $scope : 'undefined';
                                       for( $from--; $from >= 0; $from-- )
                                       {
                                          if( $cache[$from] =~ ASM_LINE )
                                          {
                                             if( $prev > 0 )
                                             {
                                                unshift @loop, ";--- $name "."-" x ( 56 - 4 - 2 - length $name )."\n" if $need_sep && $prev == $instrs_bfr;
                                                unshift @loop, $cache[$from];
                                                $prev--;
                                             }
                                             if ( $prev == 0 )
                                             {
                                                last unless $need_lines;
                                             }
                                          }
                                          elsif( $need_lines )
                                          {
                                             unshift @loop, $cache[$from];
                                             if( !( $cache[$from] =~ ASM_LINE ) ) # or file:line pattern match
                                             {
                                                unshift @loop, ";--- $name "."-" x ( 56 - 4 - 2 - length $name )."\n" if $need_sep && $prev == $instrs_bfr && ( $instrs_bfr == 0 || $from == 0 );
                                                last if $prev == 0;
                                             }
                                          }
                                       }
                                       push @loop, ";-------------------------------------------------------\n" if $need_sep && $instrs_aft != 0;
                                       $idx = $#cache+1;
                                       if( defined $src_line )
                                       {
                                          for( $idx--; $idx > $last_seen; $idx-- )
                                          {
                                             last unless $cache[$idx] =~ ASM_LINE; # or file:line pattern mismatch
                                          }
                                       }
                                       if( $idx <= $last_seen )
                                       {
                                          my $line   = $cache[$last_seen];
                                          @cache     = ();
                                          push @cache, $line;
                                          $last_seen = 0;
                                       }
                                       else
                                       {
                                          @cache     = ();
                                          $cache_on  = defined $src_line || $all_loops == TRUE;
                                          $last_seen = -1;
                                       }
                                       %addrs     = ();
                                       @hierarchy = ( [] );
                                       $jmp_tgt   = undef;
                                       $rest      = $instrs_aft;
                                    }
                                 }
                              }
                           }
                        }
                     }
                     if( $need_flow )
                     {
                        if( hex $tgt >= hex $addr )
                        {
                           if( defined $jmp_tgt && $jmp_tgt > hex $addr )
                           {
                              $jmp_tgt = hex $tgt if hex $tgt > $jmp_tgt;
                           }
                           else
                           {
                              $jmp_tgt = hex $tgt;
                           }
                        }
                        elsif( $instr =~ /^jmp[\w,]*/io && ( !defined $jmp_tgt || $jmp_tgt <= hex $addr ) )
                        {
                           #print "<eof> $addr: $line ($scope)\n";
                           $scope    = $function;
                           $inlined  = undef;
                           $openmped = undef;
                           $need_flow = FALSE;
                        }
                     }
                  }
               }
            }
         }
         if( $rest == 0 )
         {
            print @loop;
            print ";=======================================================\n";
            @loop = ();
         }
         if( $rest >= 0 ) { $rest--; }
      }
   }
}
         if( $rest >= 0 )
         {
            print @loop;
            print ";=======================================================\n";
            @loop = ();
         }
exit( 0 );
